#include "address.h"
#include <sstream>
#include <netdb.h>
#include <ifaddrs.h>
#include <stddef.h>
#include "endian.h"
#include "log.h"

// Done

// 如果T是int32_t(4字节,32位),并且您希望创建一个由30个1组成的掩码,可以这样调用函数:
// int32_t mask = CreateMask<int32_t>(30);
// 这将返回一个值,其二进制表示为11111111111111111111111111111100
template <class T>
static T CreateMask(uint32_t bits)
{
    return (1 << (sizeof(T) * 8 - bits)) - 1;
}

template <class T>
static uint32_t CountBytes(T value)
{
    uint32_t result = 0;
    for (; value; ++result)
    {
        // value &= value - 1操作会消除value最右侧的一个1
        value &= value - 1;
    }
    return result;
}

// 通过sockaddr指针创建Address
Address::ptr Address::Create(sockaddr *addr, socklen_t len)
{
    if (addr == nullptr)
        return nullptr;

    Address::ptr result;
    switch (addr->sa_family)
    {
    case AF_INET:
        result.reset(new IPv4Address(*(const sockaddr_in *)addr));
        break;
    case AF_INET6:
        result.reset(new IPv6Address(*(const sockaddr_in6 *)addr));
        break;
    default:
        result.reset(new UnknownAddress(*addr));
        break;
    }

    return result;
}

// 通过host地址返回对应条件的所有Address
bool Address::Lookup(std::vector<Address::ptr> &result, const std::string &host, int family, int type, int protocol)
{
    addrinfo hints, *results, *next;
    hints.ai_flags = 0;
    hints.ai_family = family;
    hints.ai_socktype = type;
    hints.ai_protocol = protocol;
    hints.ai_addrlen = 0;
    hints.ai_canonname = nullptr;
    hints.ai_addr = nullptr;
    hints.ai_next = nullptr;

    std::string node;
    const char *service = nullptr;

    // 检查 ipv6address service
    if (!host.empty() && host[0] == '[')
    {
        const char *endipv6 = (const char *)memchr(host.c_str() + 1, ']', host.size() - 1);
        if (endipv6)
        {
            if (*(endipv6 + 1) == ':')
            {
                service = endipv6 + 2;
            }
            node = host.substr(1, endipv6 - host.c_str() - 1);
        }
    }

    // 检查node service
    if (node.empty())
    {
        service = (const char *)memchr(host.c_str(), ':', host.size());
        if (service)
        {
            if (!memchr(service + 1, ':', host.c_str() + host.size() - service - 1))
            {
                node = host.substr(0, service - host.c_str());
                ++service;
            }
        }
    }

    if (node.empty())
    {
        node = host;
    }

    int error = getaddrinfo(node.c_str(), service, &hints, &results);
    if (error)
    {
        LOG(DEBUG, "Address::Lookup getaddress( %s , %d , %d ,%d ,%s", host, family, type, errno, strerror(errno));
        return false;
    }

    next = results;
    while (next)
    {
        result.push_back(Create(next->ai_addr, (socklen_t)next->ai_addrlen));
        // 一个ip/端口可以对应多种接字类型,比如SOCK_STREAM,SOCK_DGRAM,SOCK_RAW,所以这个会返回重复的结果
        LOG(DEBUG, "family:%s,sock type:%d", next->ai_family, next->ai_socktype);
        next = next->ai_next;
    }

    freeaddrinfo(results);
    return !result.empty();
}

// 通过host地址返回对应条件的任意Address
Address::ptr Address::LookupAny(const std::string &host, int family, int type, int protocol)
{
    std::vector<Address::ptr> res;
    if (Lookup(res, host, family, type, protocol))
    {
        return res[0];
    }
    return nullptr;
}

// 通过host地址返回对应条件的任意IPAddress
std::shared_ptr<IPAddress> Address::LookupAnyIPAddress(const std::string &host, int family, int type, int protocol)
{
    std::vector<Address::ptr> res;
    if (Lookup(res, host, family, type, protocol))
    {
        for (auto &ptr : res)
        {
            IPAddress::ptr p = std::dynamic_pointer_cast<IPAddress>(ptr);
            if (p)
            {
                return p;
            }
        }
    }
    return nullptr;
}

// 返回本机所有网卡的<网卡名, 地址, 子网掩码位数>
bool Address::GetInterfaceAddresses(std::multimap<std::string, std::pair<Address::ptr, uint32_t>> &result, int family)
{
    struct ifaddrs *next, *results;
    if (getifaddrs(&results) != 0)
    {
        LOG(DEBUG, "Address::GetInterfaceAddresses getifaddrs err = %d errstr = %s", errno, strerror(errno));
        return false;
    }

    try
    {
        for (next = results; next; next = next->ifa_next)
        {
            Address::ptr addr;
            uint32_t prefix_len = ~0u;
            if (family != AF_UNSPEC && family != next->ifa_addr->sa_family)
            {
                continue;
            }
            switch (next->ifa_addr->sa_family)
            {
            case AF_INET:
            {
                addr = Create(next->ifa_addr, sizeof(sockaddr_in));
                uint32_t netmask = ((sockaddr_in *)next->ifa_netmask)->sin_addr.s_addr;
                prefix_len = CountBytes(netmask);
            }
            break;
            case AF_INET6:
            {
                addr = Create(next->ifa_addr, sizeof(sockaddr_in6));
                in6_addr &netmask = ((sockaddr_in6 *)next->ifa_netmask)->sin6_addr;
                prefix_len = CountBytes(netmask);
            }
            break;
            default:
                break;
            }

            if (addr)
            {
                result.insert(std::make_pair(next->ifa_name, std::make_pair(addr, prefix_len)));
            }
        }
    }
    catch (...)
    {
        LOG(ERROR, "Address::GetInterfaceAddresses exception");
        freeifaddrs(results);
        return false;
    }

    return !result.empty();
}

// 获取指定网卡的地址和子网掩码位数
bool Address::GetInterfaceAddresses(std::vector<std::pair<Address::ptr, uint32_t>> &result, const std::string &iface, int family)
{
    if (iface.empty() || iface == "*")
    {
        if (family == AF_INET || family == AF_UNSPEC)
        {
            result.push_back(std::make_pair(Address::ptr(new IPv4Address()), 0u));
        }
        if (family == AF_INET6 || family == AF_UNSPEC)
        {
            result.push_back(std::make_pair(Address::ptr(new IPv6Address()), 0u));
        }
        return true;
    }

    std::multimap<std::string, std::pair<Address::ptr, uint32_t>> results;
    if (!GetInterfaceAddresses(results, family))
        return false;

    auto its = results.equal_range(iface);
    for (; its.first != its.second; ++its.first)
    {
        result.push_back(its.first->second);
    }

    return !result.empty();
}

int Address::getFamily() const
{
    return getAddr()->sa_family;
}

std::string Address::toString() const
{
    std::stringstream ss;
    insert(ss);
    return ss.str();
}

bool Address::operator<(const Address &addr) const
{
    socklen_t minlen = std::min(getAddrLen(), addr.getAddrLen());
    int result = memcmp(getAddr(), addr.getAddr(), minlen);

    if (result < 0)
    {
        return true;
    }
    else if (result > 0)
    {
        return false;
    }
    else if (getAddrLen() < addr.getAddrLen())
    {
        return true;
    }

    return false;
}

bool Address::operator==(const Address &addr) const
{
    return getAddrLen() == addr.getAddrLen() && memcmp(getAddr(), addr.getAddr(), getAddrLen()) == 0;
}

bool Address::operator!=(const Address &addr) const
{
    return !(*this == addr);
}

IPAddress::ptr IPAddress::Create(const char *address, uint16_t port)
{
    addrinfo hints, *results;
    memset(&hints, 0, sizeof(hints));

    hints.ai_flags = AI_NUMERICHOST;
    hints.ai_family = AF_UNSPEC;

    int error = getaddrinfo(address, nullptr, &hints, &results);
    if (error)
    {
        LOG(DEBUG, "IPAddress::Create( %s, %d )errno = %d errstr = %s", address, port, error, strerror(errno));
        return nullptr;
    }

    try
    {
        IPAddress::ptr result = std::dynamic_pointer_cast<IPAddress>(Address::Create(results->ai_addr, (socklen_t)results->ai_addrlen));
        if (result)
        {
            result->setPort(port);
        }
        freeaddrinfo(results);
        return result;
    }
    catch (...)
    {
        freeaddrinfo(results);
        return nullptr;
    }
}

// ------------------------------------------IPv4Address----------------------------------------

// 使用点分十进制地址创建IPv4Address
IPv4Address::ptr IPv4Address::Create(const char *address, uint16_t port)
{
    IPv4Address::ptr ret(new IPv4Address);
    ret->m_addr.sin_port = byteswapOnLittleEndian(port);

    // 用于将 IPv4 和 IPv6 地址从点分十进制（dotted-decimal）或冒号分隔的十六进制表示法转换为二进制结构的函数
    int result = inet_pton(AF_INET, address, &ret->m_addr.sin_addr);
    if (result <= 0)
    {
        LOG(DEBUG, "IPv4Address::Create( %s,%d)ret = %d errno = %d errstr = %s", address, port, result, errno, strerror(errno));
        return nullptr;
    }

    return ret;
}

// 通过sockaddr_in结构构造IPv4Address
IPv4Address::IPv4Address(const sockaddr_in &address)
{
    m_addr = address;
}

// 通过二进制地址构造IPv4Address
IPv4Address::IPv4Address(uint32_t address, uint16_t port)
{
    memset(&m_addr, 0, sizeof(m_addr));

    m_addr.sin_family = AF_INET;
    m_addr.sin_port = byteswapOnLittleEndian(port);
    m_addr.sin_addr.s_addr = byteswapOnLittleEndian(address);
}

sockaddr *IPv4Address::getAddr()
{
    return (sockaddr *)&m_addr;
}

const sockaddr *IPv4Address::getAddr() const
{
    return (sockaddr *)&m_addr;
}

socklen_t IPv4Address::getAddrLen() const
{
    return sizeof(m_addr);
}

std::ostream &IPv4Address::insert(std::ostream &os) const
{
    uint32_t addr = byteswapOnLittleEndian(m_addr.sin_addr.s_addr);

    os << ((addr >> 24) & 0xff) << "."
       << ((addr >> 16) & 0xff) << "."
       << ((addr >> 8) & 0xff) << "."
       << (addr & 0xff);

    os << ":" << byteswapOnLittleEndian(m_addr.sin_port);

    return os;
}

// 获取该地址的广播地址
// prefix_len 子网掩码位数
IPAddress::ptr IPv4Address::broadcastAddress(uint32_t prefix_len)
{
    if (prefix_len > 32)
    {
        return nullptr;
    }

    sockaddr_in baddr(m_addr);
    baddr.sin_addr.s_addr |= byteswapOnLittleEndian(~CreateMask<uint32_t>(prefix_len));

    return IPv4Address::ptr(new IPv4Address(baddr));
}

// 获取该地址的网段
IPAddress::ptr IPv4Address::networkAddress(uint32_t prefix_len)
{
    if (prefix_len > 32)
        return nullptr;

    sockaddr_in baddr(m_addr);
    baddr.sin_addr.s_addr &= byteswapOnLittleEndian(~CreateMask<uint32_t>(prefix_len));

    return IPv4Address::ptr(new IPv4Address(baddr));
}

// 获取子网掩码地址
IPAddress::ptr IPv4Address::subnetMask(uint32_t prefix_len)
{
    sockaddr_in subnet;
    memset(&subnet, 0, sizeof(subnet));

    subnet.sin_family = AF_INET;
    subnet.sin_addr.s_addr = ~byteswapOnLittleEndian(CreateMask<uint32_t>(prefix_len));

    return IPv4Address::ptr(new IPv4Address(subnet));
}

// 返回端口号
uint32_t IPv4Address::getPort() const
{
    return byteswapOnLittleEndian(m_addr.sin_port);
}

// 设置端口号
void IPv4Address::setPort(uint16_t port)
{
    m_addr.sin_port = byteswapOnLittleEndian(port);
}

// ------------------------------------------IPv6Address----------------------------------------

// 通过IPv6地址字符串构造IPv6Address
IPv6Address::ptr IPv6Address::Create(const char *address, uint16_t port)
{
    IPv6Address::ptr ret(new IPv6Address);
    ret->m_addr.sin6_port = byteswapOnLittleEndian(port);

    int result = inet_pton(AF_INET6, address, &ret->m_addr.sin6_addr);
    if (result <= 0)
    {
        LOG(DEBUG, "IPv6Address::Create( %s,%d ret = %d errno = %d errstr = %s", address, port, result, errno, strerror(errno));
        return nullptr;
    }

    return ret;
}

IPv6Address::IPv6Address()
{
    memset(&m_addr, 0, sizeof(m_addr));
    m_addr.sin6_family = AF_INET6;
}

IPv6Address::IPv6Address(const sockaddr_in6 &address)
{
    m_addr = address;
}

IPv6Address::IPv6Address(const uint8_t address[16], uint16_t port)
{
    memset(&m_addr, 0, sizeof(m_addr));
    m_addr.sin6_family = AF_INET6;
    m_addr.sin6_port = byteswapOnLittleEndian(port);
    memcpy(&m_addr.sin6_addr.s6_addr, address, 16);
}

sockaddr *IPv6Address::getAddr()
{
    return (sockaddr *)&m_addr;
}

const sockaddr *IPv6Address::getAddr() const
{
    return (sockaddr *)&m_addr;
}

socklen_t IPv6Address::getAddrLen() const
{
    return sizeof(m_addr);
}

std::ostream &IPv6Address::insert(std::ostream &os) const
{
    os << "[";
    uint16_t *addr = (uint16_t *)m_addr.sin6_addr.s6_addr;
    bool used_zeros = false;
    for (size_t i = 0; i < 8; i++)
    {
        if (addr[i] == 0 && !used_zeros)
            continue;
        if (i && addr[i - 1] == 0 && !used_zeros)
        {
            os << ":";
            used_zeros = true;
        }
        if (i)
        {
            os << ":";
        }
        os << std::hex << (int)byteswapOnLittleEndian(addr[i]) << std::dec;
    }

    if (!used_zeros && addr[7] == 0)
    {
        os << "::";
    }

    os << "]:" << byteswapOnLittleEndian(m_addr.sin6_port);
    return os;
}

// 获取该地址的广播地址
// prefix_len 子网掩码位数
IPAddress::ptr IPv6Address::broadcastAddress(uint32_t prefix_len)
{
    sockaddr_in6 baddr(m_addr);
    baddr.sin6_addr.s6_addr[prefix_len / 8] |= CreateMask<uint8_t>(prefix_len % 8);
    for (int i = prefix_len / 8 + 1; i < 16; i++)
    {
        baddr.sin6_addr.s6_addr[i] = 0xff;
    }
    return IPv6Address::ptr(new IPv6Address(baddr));
}

// 获取该地址的网段
IPAddress::ptr IPv6Address::networkAddress(uint32_t prefix_len)
{
    sockaddr_in6 baddr(m_addr);
    baddr.sin6_addr.s6_addr[prefix_len / 8] &= CreateMask<uint8_t>(prefix_len % 8);
    for (int i = prefix_len / 8 + 1; i < 16; i++)
    {
        baddr.sin6_addr.s6_addr[i] = 0x00;
    }
    return IPv6Address::ptr(new IPv6Address(baddr));
}

// 获取子网掩码地址
IPAddress::ptr IPv6Address::subnetMask(uint32_t prefix_len)
{
    sockaddr_in6 subnet;
    memset(&subnet, 0, sizeof(subnet));
    subnet.sin6_family = AF_INET6;
    subnet.sin6_addr.s6_addr[prefix_len / 8] = ~CreateMask<uint8_t>(prefix_len % 8);

    for (uint32_t i = 0; i < prefix_len / 8; i++)
    {
        subnet.sin6_addr.s6_addr[i] = 0xff;
    }

    return IPv6Address::ptr(new IPv6Address(subnet));
}

uint32_t IPv6Address::getPort() const
{
    return byteswapOnLittleEndian(m_addr.sin6_port);
}

void IPv6Address::setPort(uint16_t port)
{
    m_addr.sin6_port = byteswapOnLittleEndian(port);
}

// ------------------------------------------UnixAddress----------------------------------------

static const size_t MAX_PATH_LEN = sizeof(((sockaddr_un *)0)->sun_path) - 1;

UnixAddress::UnixAddress()
{
    memset(&m_addr, 0, sizeof(m_addr));
    m_addr.sun_family = AF_UNIX;
    m_length = offsetof(sockaddr_un, sun_path) + MAX_PATH_LEN;
}

UnixAddress::UnixAddress(const std::string &path)
{
    memset(&m_addr, 0, sizeof(m_addr));
    m_addr.sun_family = AF_UNIX;
    m_length = path.size() + 1;

    if (!path.empty() && path[0] == '\0')
    {
        --m_length;
    }

    if (m_length > sizeof(m_addr.sun_path))
    {
        throw std::logic_error("path too long");
    }
    memcpy(m_addr.sun_path, path.c_str(), m_length);
    m_length += offsetof(sockaddr_un, sun_path);
}

void UnixAddress::setAddrLen(uint32_t v)
{
    m_length = v;
}

// 重写Address中的虚函数
const sockaddr *UnixAddress::getAddr() const
{
    return (sockaddr *)&m_addr;
}

sockaddr *UnixAddress::getAddr()
{
    return (sockaddr *)&m_addr;
}

socklen_t UnixAddress::getAddrLen() const
{
    return m_length;
}

std::string UnixAddress::getPath() const
{
    std::stringstream ss;
    if (m_length > offsetof(sockaddr_un, sun_path) && m_addr.sun_path[0] == '\0')
    {
        ss << "\\0" << std::string(m_addr.sun_path + 1, m_length - offsetof(sockaddr_un, sun_path) - 1);
    }
    else
    {
        ss << m_addr.sun_path;
    }

    return ss.str();
}

std::ostream &UnixAddress::insert(std::ostream &os) const
{
    if (m_length > offsetof(sockaddr_un, sun_path) && m_addr.sun_path[0] == '\0')
    {
        return os << "\\0" << std::string(m_addr.sun_path + 1, m_length - offsetof(sockaddr_un, sun_path) - 1);
    }

    return os << m_addr.sun_path;
}

// ------------------------------------------UnknownAddress----------------------------------------

UnknownAddress::UnknownAddress(int family)
{
    memset(&m_addr, 0, sizeof(m_addr));
    m_addr.sa_family = family;
}

UnknownAddress::UnknownAddress(const sockaddr &addr)
{
    m_addr = addr;
}

// 重写Address中的虚函数
const sockaddr *UnknownAddress::getAddr() const
{
    return (sockaddr *)&m_addr;
}

sockaddr *UnknownAddress::getAddr()
{
    return (sockaddr *)&m_addr;
}

socklen_t UnknownAddress::getAddrLen() const
{
    return sizeof(m_addr);
}

std::ostream &UnknownAddress::insert(std::ostream &os) const
{
    os << "[UnknownAddress family = " << m_addr.sa_family << "]";
    return os;
}

std::ostream &operator<<(std::ostream &os, const Address &addr)
{
    return addr.insert(os);
}
